(() => {
    let renderer  // 渲染器
    let scene  // 场景
    let camera  // 相机
    let stats  // 资源监视器
    let helperBox 
    let lightBox 
    let managerBox  // 加载器
    let pageDestory // 页面是否要销毁
    let objBox = {} // 物体对象盒子
    let stage 
    let box
    let gui  // gui
    let guiParam // gui的参数
    let clock  // 世界时钟
    let orbitControls // 相机控件
    let animationFrame  // 动画
    // 初始
    let init = () => {
        initRender()
        initScene()
        initCamera()
        initClock()
        initManager()
        initLoader()
        initObj()
        initLight()
        initHelper()
        initStats()
        initOrbitControls()
        updateRenderer()
    }
    // 初始渲染器
    let initRender = () => {
        renderer = new THREE.WebGLRenderer({antialias: true}) // 渲染器
        renderer.setSize(window.innerWidth, window.innerHeight)
        renderer.shadowMap.enabled = true // 渲染器阴影渲染
        renderer.shadowMap.type = THREE.PCFSoftShadowMap // 阴影类型
        document.getElementById('three-canvas').appendChild(renderer.domElement)
        renderer.setClearColor('rgb(15, 1, 25)')
    }
    // 渲染场景
    let initScene = () => {
        scene = new THREE.Scene() // 场景
    }
    // 渲染时钟
    let initClock = () => {
        clock = new THREE.Clock() // 世界时钟
    }
    // 光源
    let initLight = () => {
        lightBox = {
        ambientLight: new THREE.AmbientLight('rgb(50, 50, 50)'), // 环境光
        spotLight: new THREE.SpotLight('rgb(255, 255, 255)', 1.5, 800, Math.PI / 180 * 45, 0.3, 0) // 半球光
        }
        lightBox.spotLight.position.set(300, 600, 300)
        lightBox.spotLight.castShadow = true
        scene.add(lightBox.spotLight)
        scene.add(lightBox.ambientLight)
    }
    // 初始相机
    let initCamera = () => {
        camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 10000) // 相机
        camera.position.x = 800
        camera.position.y = 500
        camera.position.z = 800
        camera.up.x = 0
        camera.up.y = 1
        camera.up.z = 0
    }
    // 初始加载管理器
    let initManager = () => {
        // 加载器配置
        let long = 600
        let y = 0
        // 加载器公共事件
        let managerEvent = (name, manager) => {
        manager.onStart = (url, itemsLoaded, itemsTotal) => {
            y += 60
            let geometry = new THREE.BoxBufferGeometry(long, 40, 40, 4, 4)
            let material = new THREE.MeshStandardMaterial({
            color: `rgb(125, 255, 255)`,
            roughness: 0,
            metalness: 0
            })
            objBox[name] = new THREE.Mesh(geometry, material)
            objBox[name].castShadow = true
            objBox[name].receiveShadow = true
            objBox[name].position.set(0, y, 0)
            objBox[name].scale.x = 0.01
            scene.add(objBox[name])
            objBox[name + 'BoxLine'] = new THREE.LineSegments(new THREE.EdgesGeometry(geometry), new THREE.LineBasicMaterial({ color: 0xffffff }))
            objBox[name + 'BoxLine'].position.set(0, y, 0)
            scene.add(objBox[name + 'BoxLine'])
        }
        manager.onLoad = () => {
            scene.remove(objBox[name])
            scene.remove(objBox[name + 'BoxLine'])
        }
        manager.onProgress = (url, itemsLoaded, itemsTotal) => {
        }
        manager.onError = url => {
            // 兼容未加载完进行路由跳转
            if (objBox) {
            objBox[name + 'BoxLine'].material.color.setHex('rgb(255, 0, 0)')
            }
        }
        }
        // 初始加载器
        managerBox = {
        OBJManager: new THREE.LoadingManager(),
        MTLManager: new THREE.LoadingManager()
        }
        managerEvent('OBJManager', managerBox.OBJManager)
        managerEvent('MTLManager', managerBox.MTLManager)
    }
    // 初始加载器
    let initLoader = () => {
        let OBJLoader = new THREE.OBJLoader(managerBox.OBJManager)
        let MTLLoader = new THREE.MTLLoader(managerBox.MTLManager)// 材质文件加载器
        MTLLoader.load(
        './model/cake.mtl',
        material => {
            OBJLoader.setMaterials(material)
            OBJLoader.load(
            './model/cake.obj',
            mesh => {
            // mesh还未加入场景中预处理
                mesh.translateX(-40)
                mesh.translateZ(150)
                // 开启所有mesh的阴影
                mesh.children.forEach(elem => {
                elem.castShadow = true
                elem.receiveShadow = true
                })
                objBox.cake = mesh
                scene.add(objBox.cake)
                // mesh 载入完成载入gui
                initGui()
            },
            xhr => {
                if (pageDestory) {
                xhr.target.abort()
                } else {
                objBox.OBJManager.scale.x = xhr.loaded / xhr.total
                }
            }
            )
        },
        xhr => {
            if (pageDestory) {
            xhr.target.abort()
            } else {
            objBox.MTLManager.scale.x = xhr.loaded / xhr.total
            }
        }
        )
    }
    // 初始物体
    let initObj = () => {
        let geometry = new THREE.BoxGeometry(600, 10, 600, 4, 4)
        let material = new THREE.MeshLambertMaterial({color: 'rgb(45, 0, 50)'})
        objBox.stage = new THREE.Mesh(geometry, material)
        objBox.stage.castShadow = true
        objBox.stage.receiveShadow = true
        objBox.stage.position.set(0, 5, 0)
        scene.add(objBox.stage)
    }
    // 初始辅助
    let initHelper = () => {
        helperBox = {
        axesHelper: {helper: new THREE.AxesHelper(10000)}, // 坐标轴
        gridHelper: {helper: new THREE.GridHelper(1500, 30, 'white', 'rgb(150, 150, 150)')}, // 网格
        spotLightHelper: {helper: new THREE.SpotLightHelper(lightBox.spotLight)} // 聚光灯
        }
        scene.add(helperBox.spotLightHelper.helper)
        scene.add(helperBox.axesHelper.helper)
        scene.add(helperBox.gridHelper.helper)
    }
    // 初始监视器
    let initStats = () => {
        stats = new Stats() // 资源监视器
        stats.setMode(0)
        stats.domElement.id = 'three-stats'
        stats.domElement.style.position = 'fixed'
        stats.domElement.style.left = '0px'
        stats.domElement.style.top = '0px'
        document.body.appendChild(stats.domElement)
    }
    // 初始控制台
    let initGui = () => {
        gui = new dat.GUI() // 控制台
        guiParam = { // 控制参数
            showSpotLightHelper: true,
            showAxesHelper: true,
            showGridHelper: true,
            cameraRotate: orbitControls.autoRotate,
            cameraPositionX: camera.position.x,
            cameraPositionY: camera.position.y,
            cameraPositionZ: camera.position.z,
            cameraTargetX: orbitControls.target.x,
            cameraTargetY: orbitControls.target.y,
            cameraTargetZ: orbitControls.target.z,
            castShadow: lightBox.spotLight.castShadow,
            lightRotate: true,
            color: lightBox.spotLight.color.getHex(),
            intensity: lightBox.spotLight.intensity,
            distance: lightBox.spotLight.distance,
            angle: lightBox.spotLight.angle * 180 / Math.PI,
            penumbra: lightBox.spotLight.penumbra,
            decay: lightBox.spotLight.decay,
            positionX: lightBox.spotLight.position.x,
            positionY: lightBox.spotLight.position.y,
            positionZ: lightBox.spotLight.position.z,
            objPositionX: objBox.cake.position.x,
            objPositionY: objBox.cake.position.y,
            objPositionZ: objBox.cake.position.z,
            objRotationX: objBox.cake.rotation.x * 180 / Math.PI,
            objRotationY: objBox.cake.rotation.y * 180 / Math.PI,
            objRotationZ: objBox.cake.rotation.z * 180 / Math.PI,
        }
        let helperSetting = gui.addFolder('helper setting')
        helperSetting.add(guiParam, 'showSpotLightHelper').onChange(data => {
            if (data) {
            scene.add(helperBox.spotLightHelper.helper)
            } else {
            scene.remove(helperBox.spotLightHelper.helper)
            }
        })
        helperSetting.add(guiParam, 'showAxesHelper').onChange(data => {
            if (data) {
            scene.add(helperBox.axesHelper.helper)
            } else {
            scene.remove(helperBox.axesHelper.helper)
            }
        })
        helperSetting.add(guiParam, 'showGridHelper').onChange(data => {
            if (data) {
            scene.add(helperBox.gridHelper.helper)
            } else {
            scene.remove(helperBox.gridHelper.helper)
            }
        })
        helperSetting.open()
        let cameraSetting = gui.addFolder('camera setting')
        cameraSetting.add(guiParam, 'cameraRotate').onChange(data => {
            orbitControls.autoRotate = data
        })
        cameraSetting.add(guiParam, 'cameraPositionX', -500, 500, 1).onChange(data => {
            camera.position.x = data
        })
        cameraSetting.add(guiParam, 'cameraPositionY', -500, 500, 1).onChange(data => {
            camera.position.y = data
        })
        cameraSetting.add(guiParam, 'cameraPositionZ', -500, 500, 1).onChange(data => {
            camera.position.z = data
        })
        cameraSetting.add(guiParam, 'cameraTargetX', -500, 500, 1).onChange(data => {
            orbitControls.target.x = data
        })
        cameraSetting.add(guiParam, 'cameraTargetY', -500, 500, 1).onChange(data => {
            orbitControls.target.y = data
        })
        cameraSetting.add(guiParam, 'cameraTargetZ', -500, 500, 1).onChange(data => {
            orbitControls.target.z = data
        })
        cameraSetting.open()
        let lightSetting = gui.addFolder('Light setting')
        lightSetting.add(guiParam, 'castShadow').onChange(data => {
            lightBox.spotLight.castShadow = data
        })
        lightSetting.add(guiParam, 'lightRotate')
        lightSetting.addColor(guiParam, 'color', -500, 500).onChange(data => {
            lightBox.spotLight.color.setHex(data)
            helperBox.spotLightHelper.helper.update()
        })
        lightSetting.add(guiParam, 'intensity', 0, 10).onChange(data => {
            lightBox.spotLight.intensity = data
            helperBox.spotLightHelper.helper.update()
        })
        lightSetting.add(guiParam, 'distance', 0, 1000, 1).onChange(data => {
            lightBox.spotLight.distance = data
            helperBox.spotLightHelper.helper.update()
        })
        lightSetting.add(guiParam, 'angle', 0, 60, 1).onChange(data => {
            lightBox.spotLight.angle = Math.PI / 180 * data
            helperBox.spotLightHelper.helper.update()
        })
        lightSetting.add(guiParam, 'penumbra', 0, 1, 0.1).onChange(data => {
            lightBox.spotLight.penumbra = data
            helperBox.spotLightHelper.helper.update()
        })
        lightSetting.add(guiParam, 'decay', 0, 5, 0.1).onChange(data => {
            lightBox.spotLight.decay = data
            helperBox.spotLightHelper.helper.update()
        })
        lightSetting.add(guiParam, 'positionY', -500, 500, 1).onChange(data => {
            lightBox.spotLight.position.y = data
            helperBox.spotLightHelper.helper.update()
        })
        lightSetting.add(guiParam, 'positionX', -500, 500, 1).onChange(data => {
            lightBox.spotLight.position.x = data
            helperBox.spotLightHelper.helper.update()
        })
        lightSetting.add(guiParam, 'positionZ', -500, 500, 1).onChange(data => {
            lightBox.spotLight.position.z = data
            helperBox.spotLightHelper.helper.update()
        })
        lightSetting.open()
        let objSettting = gui.addFolder('Obj setting')
        objSettting.add(guiParam, 'objPositionX', -500, 500, 1).onChange(data => {
            objBox.cake.position.x = data
        })
        objSettting.add(guiParam, 'objPositionY', -500, 500, 1).onChange(data => {
            objBox.cake.position.y = data
        })
        objSettting.add(guiParam, 'objPositionZ', -500, 500, 1).onChange(data => {
            objBox.cake.position.z = data
        })
        objSettting.add(guiParam, 'objRotationX', -180, 180, 1).onChange(data => {
            objBox.cake.rotation.x = data * Math.PI / 180
        })
        objSettting.add(guiParam, 'objRotationY', -180, 180, 1).onChange(data => {
            objBox.cake.rotation.y = data * Math.PI / 180
        })
        objSettting.add(guiParam, 'objRotationZ', -180, 180, 1).onChange(data => {
            objBox.cake.rotation.z = data * Math.PI / 180
        })
        objSettting.open()
    }
    // 加载相机插件
    let initOrbitControls = () => {
        orbitControls = new THREE.OrbitControls(camera, renderer.domElement)
        orbitControls.autoRotate = true // 是否开启相机自动旋转
        orbitControls.target = new THREE.Vector3(0, 120, 0) // 初始相机的所看的位置
    }
    // 动画
    let animation = () => {
        if (guiParam && guiParam.lightRotate) {
            let r = 400
            let deg = Date.now() * 0.001
            lightBox.spotLight.position.x = -Math.cos(deg) * r
            lightBox.spotLight.position.z = Math.sin(deg) * r
            helperBox.spotLightHelper.helper.update()
        }
    }
    // 加载场景
    let updateRenderer = () => {
        orbitControls.update(clock.getDelta())
        renderer.render(scene, camera)
        animation()
        stats.update()
        animationFrame = requestAnimationFrame(updateRenderer)
    }

    init()
})()